"""
Copyright (c) 2012-2013 Alexandru Emilian Susu (icam.service@gmail.com).
    All rights reserved.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
    ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    SUCH DAMAGE.

----------------------------------------------------------------
PPP (Python PreProcessor),
  a sort of equivalent tool to cpp for Python
    (no preprocessor directives, just standard Python)
----------------------------------------------------------------

To achieve cross-platform development in Python (in iCam.py) we used standard
        Python if-elif-else conditional statments.
    Example:
      if ANDROID_OS:
          ...
      elif iOS_PYOBJC:
          ...

    We could have used:
        - the standard cpp preprocessor with its directives;
            Using this solution would have made the Python code a bit
              more difficult to write and read:
                #define ANDROID_OS
                #ifdef ANDROID_OS
                    ANDROID_OS = True
                #endif
        - Python decorators?;
        - ...

So, our current if-elif-else Python cross-platform directives is a decent, 
    straight-forward imitation (although with some penalty at runtime) of
    #ifdef-#elif-#else conditional C preprocessor directives.
  However, the advantages are: maintain one/the same codebase for
        different platforms and reuse code as much as possible.

How do we specify what is the criteria to remove code:
  First, we use the list PREDICATE_TO_KEEP_BLOCK
    to specify the predicates (e.g., the platforms) we target NOT to remove.
  Clearly, a program can have many predicates in its conditional statements,
      such as vars that are related to the actual/"inner" code itself.
    We use
        PREDICATE_TO_REMOVE_BLOCK to
           specify which if/elif blocks we can remove.

During a pass (call to VisitAST) we remove as much "dead code" (following 
    the criterias specified above) as possible.
  However, when we change a statement, the children of that statement are
    skipped (we defer the work in another pass). This is why we have several
    passes duing a transformation - we run until we reach fixpoint: no more
    changes happen during a pass. We also put an upper bound on the total
    number of passes, NUM_MAX_PASSES, after which we exit the program.
The script also adds automatically "pass" Python statements (= NOP) in the
  blocks without body (e.g., if statements, try-except).

Usage example:
    python ppp.py script_to_preprocess.py >myStdout
  It outputs a lot of info on the stdout.
  At the end, it writes a file script_to_preprocess.py.ppp
        containing the transformed script.

For a complete treatment, best is to remove first the comments from the
    Python script.
  To preprocess run:
    python ppp.py script_to_preprocess.py 1>preprocessed_script.py
  Note that the preprocessed_script.py contains empty lines.
  That's why we have to give:
    ./post_ppp.sh script_to_preprocess.py >preproced_script.py!!!!


See http://docs.python.org/library/parser.html:
    "The parser module provides an interface to Python's internal parser and
        byte-code compiler.
        The primary purpose for this interface is to allow Python code to edit
            the parse tree of a Python expression and create executable code
            from this.
        This is better than trying to parse and modify an arbitrary Python code
            fragment as a string because parsing is performed in a manner
            identical to the code forming the application.
        It is also faster."
    "Note: From Python 2.5 onward, it's much more convenient to cut in at the
        Abstract Syntax Tree (AST) generation and compilation stage, using the
        ast module."
"""



MY_DEBUG_STDOUT = False
#MY_DEBUG_STDOUT = True

MY_DEBUG_STDERR = True

"""
# NO LONGER REQUIRED, it seems:
# For some scripts (e.g., test10.py) it is better to give return.
#RETURN_AFTER_CHANGE_AST = False
"""

"""
Note:
    - when modifying (especially when removing) the AST you can mess-up the
        subsequent steps (iterators?) of the AST visit (in the sense that you
        can skip parts of the AST - e.g., test10.py, test2.py, test4.py), so:
        - traverse as much as possible the AST and when the traversal is finished
            start it again, until no more changes happen
            - we can do this within Python (in 1 run of the script) OR running
                again and again ppp.py
        - another somewhat more complicated solution is to create another AST,
            but this requires to do copy.deep_copy on the AST nodes.

        Note that the result of the preprocessed script might require
                adding a few "pass" statements.
        For USING_AST = True we have:
            CheckIfParentEmptyToAddPass
        For USING_AST = Fale we have:
            ShouldWeWritePass - check if pass is inside function.
"""

"""
 300 should be enough - even iCam.py reaches "fixed point" in ~10 passes of
    VisitAST()
"""
NUM_MAX_PASSES = 300 #10 #3 #7 #300
numPass = 0

USING_AST = True


"""
TODO:
    - better evaluation of predicates (iOS == True seems to work)
        - use instead of
            PREDICATE_TO_REMOVE_BLOCK and PREDICATE_TO_KEEP_BLOCK
          the
            PREDICATE_VALUES (map with values: "ANDROID_OS": True, etc)
          and interpret in ExpressionSatisfiesRemovalConstraints()
              the test expression of the if statement 
                (directly with exec(testExprStr),
                    but there could be still unknown predicates - maybe??
                        use SMT solver?)
          - interpret if predicates : handle "if ANDROID_OS is False:",
                "if WINDOWS_OS == False:", etc not AND...

    - changing/mutating the AST can result in bad traversals, later,
            in codegen.to_source
        - therefore, I added 1 pass statement to the if/elif branches complying
                to the predicate, that are supposed to be removed.
            Then I can rely on the Python compiler that can do
                dead-code elimination.
        - later, I changed succesfully the AST

    - codegen
      From Z:\1PhD\ReVival\Python\ppp\11Other_interesting_modules\codegen\Good_somewhat\codegen.py OR
           Z:\1PhD\ReVival\Python\ppp\11Other_interesting_modules\codegen\codegen_Alex.py
        added an inexisting else in the original source code: - see
            else:
            wakawaka = 2
                 VisitAST(): at entrance,
                    aNode = <_ast.If object at 0x022F1230> ast.iter_fields() =
                        ('test', <_ast.Name object at 0x022F1250>)
                        ('body', [<_ast.Assign object at 0x022F1270>,
                            <_ast.Assign object at 0x022F12D0>,
                            <_ast.Pass object at 0x022F1330>])  ('orelse', [])
        It seems all ifs are at
            Real_deepcopy(): else if copier branch, else if issc, if copier
                branch, else if reductor branch, if reductor branch.

        aNode.body = [<_ast.Assign object at 0x022F1270>,
                        <_ast.Assign object at 0x022F12D0>,
                        <_ast.Pass object at 0x022F1330>]
                if (iOS3):
                    lll = 1
                    mmm = 1
                    pass


There are a few standard ways to parse Python code:
    - using the parser module - we no longer support this, since we consider
        the ast module to be more suited for proper AST traversal, etc

    - using the ast module - this has the advantage it has the codegen.py
       (that pretty prints Python code from the AST generated with this module)
        Ops allowed to change the AST:
            - ops on body:
                aNode.body = []
                aNode.body.insert(), remove()
            - parentNode.orelse = aNode.orelse
            - parentNode.orelse = []

    - a third one (UNDERSTAND pros and cons!!!!) is the compiler.ast module
       (see http://docs.python.org/2/library/compiler.html#module-compiler.ast)
        It is used in http://pypi.python.org/pypi/PythonTidy/1.22, where it
        does parsing, transformation and pretty-printing.

  A good comparison of the first two modules can be found at
            http://docs.python.org/library/parser.html:
    "The parser module provides an interface to Python's internal parser and
        byte-code compiler."
    "From Python 2.5 onward, it's much more convenient to cut in at the
        Abstract Syntax Tree (AST) generation and compilation stage, using
        the ast module.
     The parser module exports the names documented here also with "st"
        replaced by "ast"; this is a legacy from the time when there was no
        other AST and has nothing to do with the AST found in Python 2.5.
      This is also the reason for the functions' keyword arguments being
        called ast, not st.
      The "ast" functions have been removed in Python 3."

    The problem with parser module is that we are able to access the AST from the
        parser module with the ast2tuple() method.
    And the tuple is immutable so we have to rewrite the entire program, which
        complicates a bit things.
    Having tuples makes things difficult:
        - the immediate parent tuple is not meaningful - it is normally not the
            parent node.


Sept 9, 2012 - decided to use the ast module instead, which provides a
    proper AST

Todo:
    For the version USING_AST (using the ast module):
        !!!!Figure out the best way to takeout the if-elif-else with the
            desired predicates.

    For the version using the parser module:
        find meaningful parent tuple.
        !!!!Use (if useful) ast2list() or st2list() instead of ast2tuple().
Done:
    - Pretty print the AST after the transformation
        see maybe Z:\1PhD\ReVival\Python\ppp\11Other_interesting_modules\asteval\asteval-0.9.tar.gz
            for inspiration see \asteval-0.9\tests\unittest_1.py for
            usage example.
"""

import sys
import types
import symbol
import token
import tokenize
import compiler
import random
import symtable
import StringIO
import getopt
import re
import os
import traceback

import ast
import parser

import codegen
import copy



#!!!!TODO Interpret predicates; MAYBE check for things like ANDROID_OS is False
# aString is a tuple (e.g., ('id', 'ANDROID_OS') for USING_AST).
def ExpressionSatisfiesRemovalConstraints(aString, testExpression):
    global PREDICATE_TO_KEEP_BLOCK, \
           PREDICATE_TO_REMOVE_BLOCK

    if MY_DEBUG_STDOUT:
        print "Entered ExpressionSatisfiesRemovalConstraints(): aString=%s." % \
                                                                str(aString)
        #print "aString =", aString
        print "testExpression =", testExpression
        print "testExpression = %s." % codegen.to_source(testExpression)

    for aStrElem in PREDICATE_TO_KEEP_BLOCK:
        if USING_AST:
            #!!!!Check also for more complex predicates, like iOS == True
            #   (which is an ast.Compare) - interpret (do a DFS traversal
            #   of the AST), or maybe use codegen.to_source()
            # An element of
            #    PREDICATE_TO_KEEP_BLOCK was
            #       found in the expression.
            if isinstance(aString, tuple) and (aString[0] == "id") and \
                                                    (aString[1] == aStrElem):
                #print "It does NOT satisfy removal constraints!"
                return False

            # TODO: !!!!!!!!Check for predicates with or, and, etc. - use SMT (Z3) maybe (take any of the predicates p from the list and test p |= testExpression)
            elif isinstance(testExpression, ast.Compare):
                #print "Am at isinstance(testExpression, ast.Compare)"
                for childNode in ast.iter_child_nodes(testExpression):
                    #print "childNode =", childNode
                #for childNode in ast.iter_fields(testExpression):
                    for fieldNode in ast.iter_fields(childNode):
                        #print "fieldNode =", fieldNode
                        if isinstance(fieldNode, tuple) and \
                                        (fieldNode[0] == "id") and \
                                        (fieldNode[1] == aStrElem):
                            return False
                            """
                            print "Condition passed"
                            print "fieldNode[0] =", fieldNode[0]
                            print "fieldNode[1] =", fieldNode[1]
                            print "fieldNode[0] == id =", fieldNode[0] == "id"
                            print "fieldNode[0] == aStrElem =", fieldNode[0] == aStrElem
                            if (fieldNode[0] == "id") and (fieldNode[1] == aStrElem):
                                print "YUP"
                                return False
                            """

    for aStrElem in PREDICATE_TO_REMOVE_BLOCK:
        if USING_AST:
            #!!!!Check also for more complex predicates, like iOS == True
            #   (which is an ast.Compare) - interpret (do a DFS traversal
            #    of the AST), or maybe use codegen.to_source()

            #if MY_DEBUG_STDOUT:
            #    print "ExpressionSatisfiesRemovalConstraints(): " \
            #           "myStrIndex1 =", myStrIndex1
            #if aString == aStrElem:
            if isinstance(aString, tuple) and (aString[0] == "id") and \
                                                    (aString[1] == aStrElem):
                break
            elif isinstance(testExpression, ast.Compare):
                for childNode in ast.iter_child_nodes(testExpression):
                    for fieldNode in ast.iter_fields(childNode):
                        """
                        print "fieldNode[0] =", fieldNode[0]
                        print "fieldNode[1] =", fieldNode[1]
                        print "aStrElem =", aStrElem
                        """
                        if isinstance(fieldNode, tuple) and \
                                            (fieldNode[0] == "id") and \
                                            (fieldNode[1] == aStrElem):
                            #print "Found, so return True"
                            #break
                            return True
                """
                for childNode in ast.iter_child_nodes(testExpression):
                    for fieldNode in ast.iter_fields(childNode):
                        if isinstance(fieldNode, tuple) and \
                                        (fieldNode[0] == "id") and \
                                        (fieldNode[1] == aStrElem):
                            break
                """

                """
                for childNode2 in ast.iter_child_nodes(testExpression):
                    for childNode in ast.iter_child_nodes(childNode2):
                        print "childNode =", childNode
                        if isinstance(childNode, list) and \
                                (childNode[0] == "id") and \
                                (childNode[1] == aStrElem):
                            break
                """
    else:
        # Executed if break in for loop was not used.
        return False

    return True

    """
    #myStrIndex1 = str(aString).find("(1, '" +
    #       PREDICATE_TO_KEEP_BLOCK + "',")

    myStrIndex2 = str(aString).find("(1, '" +
            PREDICATE_TO_KEEP_BLOCK + "',")

    if (myStrIndex1 != -1) and (myStrIndex2 == -1):
        return True

    return False
    """


#############################################################################
#############################################################################
####################USING_AST = True (using module ast)######################
#############################################################################
#############################################################################


def GetElementsOfIf(aNode, howMuchIndent):
    elem = ast.iter_fields(aNode)

    if MY_DEBUG_STDOUT:
        for myIndex in range(howMuchIndent + 1):
            sys.stdout.softspace = 0
            print "    ",
        print "elem =", elem
        #print "dir(elem) =", dir(elem)

    test = None
    body = None
    orelse = None

    try:
        test = elem.next()
        body = elem.next()
        orelse = elem.next()

        if MY_DEBUG_STDOUT:
            for myIndex in range(howMuchIndent + 1):
                sys.stdout.softspace = 0
                print "    ",
            #print "elem.next() =", test
            print "test =", test
            for myIndex in range(howMuchIndent + 1):
                sys.stdout.softspace = 0
                print "    ",
            print "body =", body
            for myIndex in range(howMuchIndent + 1):
                sys.stdout.softspace = 0
                print "    ",
            print "orelse =", orelse
    #Inspired from http://stackoverflow.com/questions/1292189/how-does-python-for-loop-work
    except StopIteration:
        # StopIteration exception is raised after last element
        pass #break

    return (test, body, orelse)





passChangedAST = False

def CheckIfParentEmptyToAddPass(aNodeParent, aNode):
    if aNodeParent.body == []:
        myStmt = ast.Pass()
        aNodeParent.body = [ myStmt ]

def VisitAST(aNode, aNodeParent, howMuchIndent = 0):
    global passChangedAST

    if MY_DEBUG_STDOUT:
        for myIndex in range(howMuchIndent):
            sys.stdout.softspace = 0
            print "    ",

        myStr = "VisitAST(): at entrance aNode = %s, aNodeParent = %s." % \
                                            (str(aNode), str(aNodeParent))
        print myStr,
        #sys.stdout.flush()

        """
        From http://docs.python.org/library/ast.html:
            "Yield a tuple of (fieldname, value) for each field in
                node._fields that is present on node."
        """
        print "ast.iter_fields() = ",
        for nodeField in ast.iter_fields(aNode):
            print str(nodeField) + " ",
        print

        try:
            print "aNode.body = %s" % aNode.body
        except:
            pass


    if isinstance(aNode, ast.If):
        if MY_DEBUG_STDOUT:
            for myIndex in range(howMuchIndent):
                sys.stdout.softspace = 0
                print "    ",

            print "VisitAST(): Yabba. aNodeParent = %s, " \
                    "ast.iter_fields(aNode) = %s" % \
                    (str(aNodeParent), str(ast.iter_fields(aNode)))

        """
        Note that for an if-elif-else construct (with only one elif), we have
                an Ast.If object (for the if-elif-else) with:
            - test, being the predicate,
            - body, being the list of statements,
            - orelse, being the elif Ast.If object, 
              which itself has: 
                - test and body, for the elif branch 
                - orelse, being the else, with orelse[1] containing the list of
                        statements.
        """

        #print "elem.next().count =", elem.next().count(0)

        # Gives exception:
        #print "elem.next() =", elem.next()


        # We use the for loop just to retrieve the first field .
        for nodeField2 in ast.iter_fields(aNode.test):
            if MY_DEBUG_STDOUT:
                #print "                     " + str(nodeField2) + " ",
                print "                     nodeField2 = %s" % str(nodeField2)

            break

        if ExpressionSatisfiesRemovalConstraints(nodeField2, aNode.test):
            passChangedAST = True

            if MY_DEBUG_STDOUT:
                for myIndex in range(howMuchIndent):
                    sys.stdout.softspace = 0
                    print "    ",
                print "VisitAST(): aNode has predicate satisfying constraints."

            # Rewrite the AST!!!!:
            if MY_DEBUG_STDOUT:
                for myIndex in range(howMuchIndent):
                    sys.stdout.softspace = 0
                    print "    ",

                print "VisitAST(): Removing aNode %s." % (str(aNode))

            """
            !!!!Figure out a way to transform the AST: in this case, to
                remove the if.
            Doing this messes up the ast.iter_child_nodes(aNode) used to
                recursively traverse the AST.

            #aNode = orelse
            """
            if MY_DEBUG_STDOUT:
                print "ast.If statement - aNode.body = %s, " \
                        "aNode.orelse = %s, " \
                        "aNodeParent = %s, " \
                        "aNodeParent.body = %s, " % \
                        (aNode.body, aNode.orelse, aNodeParent, \
                                                aNodeParent.body)
                if isinstance(aNodeParent, ast.If):
                    print "    aNodeParent.orelse = %s" % aNodeParent.orelse
                #print "aNode.body[0].body = %s" % aNode.body[0].body

            """
            We replace the block associated with the if statement with the
                matched predicate with a pass.
            !!!!We can then do dead code elimination, although I am not
                able to find any such tool.
            """



            """
            - The matching if statement (which we need to remove):
              - does NOT have orelse (aNode.orelse == [])
                  - Case 1: has If parent - see code for subcases

                  - Case 2: does NOT have If parent
                      aNodeParent.body.remove(aNode)


              - has orelse (aNodeOrelse != [])
                  - Case 3: has If parent
                      aNodeParent.orelse = aNode.orelse
                      Ex:
                          if parentPredicate:
                              ...
                          elif matchingPredicate:
                              ...
                          elif orelsePredicate:
                              ...


                  - Case 4: does NOT have If parent
                      Ex:
                          if matchingPredicate:
                              ...
                          elif orelsePredicate:
                              ...

                      - we insert in aNodeParent.body, at the right place:
                          - if orelse is an ast.If:
                              we insert this particular ast.If
                          - if orelse is an else:
                              we insert a list of statements
            """




            #if MY_DEBUG_STDOUT:
            #    print "Am here1"

            if aNode.orelse == []:
                """
                if MY_DEBUG_STDOUT:
                    print "orelse is [], so we give aNode.body = [ myStmt ]"
                myStmt = ast.Pass()
                aNode.body = [ myStmt ]
                """

                if isinstance(aNodeParent, ast.If):
                    """
                    Case 1: We are in the case aNode is an elif, without else.

                    Case 1.a
                        if parentPredicate:
                            ...
                        elif matchingPredicate:
                            ...
                      OR

                    Case 1.b
                        if parentPredicate:
                            ...
                            if matchingPredicate:
                                ...
                    """
                    if (len(aNodeParent.orelse) == 1) and \
                                            (aNodeParent.orelse[0] == aNode):
                        """
                        Case 1.a
                            if parentPredicate:
                                ...
                            elif matchingPredicate:
                                ...
                            #no else/elif
                        """
                        print "Replacement case #1.a"

                        aNodeParent.orelse = []
                    else:
                        if len(aNodeParent.body) == 1:
                            if aNodeParent.body[0] == aNode: #!!!!TODO: this is kind of redundant check
                                # We must not forget that aNode.orelse == []

                                """
                                Case 1.b:
                                if parentPredicate:
                                    if matchingPredicate:
                                        ...
                                    #no else/elif
                                    ...
                                """
                                print "Replacement case #1.b"

                                #aNodeParent.body = []

                                myStmt = ast.Pass()
                                aNodeParent.body = [ myStmt ]
                        else:
                            # We must not forget that aNode.orelse == []
                            # We are in case len(aNodeParent.body) > 1
                            #   (can't be 0 for sure, since it contains aNode):
                            if aNodeParent.body.count(aNode) != 0:
                                """
                                Case 1.b.1:
                                if parentPredicate:
                                    ...
                                    if matchingPredicate:
                                        ...
                                    #no else/elif
                                    ...
                                """
                                print "Replacement case #1.b.1"

                                aNodeParent.body.remove(aNode)
                                CheckIfParentEmptyToAddPass(aNodeParent, aNode)
                            else:
                                """
                                Case 1.b.2:
                                if parentPredicate:
                                    ...
                                else:
                                    ...
                                    if matchingPredicate:
                                        ...
                                    ...
                                """
                                print "Replacement case #1.b.2"

                                if len(aNodeParent.orelse) != 0:
                                    aNodeParent.orelse.remove(aNode)
                                    CheckIfParentEmptyToAddPass(aNodeParent, \
                                                                aNode)
                else:
                #if (not isinstance(aNodeParent, ast.If)):
                    """
                    Case 2
                        if matchingPredicate:
                            ...
                        #no else/elif
                        ...
                    """
                    print "Replacement case #2"

                    #try:
                    aNodeParent.body.remove(aNode)
                    CheckIfParentEmptyToAddPass(aNodeParent, aNode)
                    #except:
            else:
            #if aNode.orelse != []:
                case3B = False

                if isinstance(aNodeParent, ast.If):
                    if (len(aNodeParent.orelse) == 1) and \
                                (aNodeParent.orelse[0] == aNode):
                                # This is correct - read comments at
                                #   else branch below
                        """
                        Case 3.a: The parent is an if (as well).
                        We are in the case:
                            if parentPredicate:
                                ...
                            elif matchingPredicate:
                                ...
                            el...:
                                ...
                        """
                        print "Replacement case #3.a"

                        #aNodeParent.orelse = copy.deepcopy(aNode.orelse[0].orelse) 
                        #!!!! 
                        aNodeParent.orelse = copy.deepcopy(aNode.orelse)
                    else:
                        """
                         We are in case len(aNodeParent.orelse) != 1, most
                                                                    likely 0:
                          OR in case (len(aNodeParent.orelse) == 1) and \
                                        (aNodeParent.orelse[0] != aNode):
                          For BOTH cases it applies.
                        """
                        if len(aNodeParent.body) >= 1:
                            """
                            Case 3.b: The parent is an if (as well).
                            We are in the case:
                                if parentPredicate:
                                    ... #(in case len(aNodeParent.body) > 1)
                                    if matchingPredicate:
                                    ...
                                    el..:
                                    ...
                                el...: (if aNodeParent.orelse[0] != aNode)
                            """
                            case3B = True
                            print "Replacement case #3.b"

                # Cases 4 and 3b are rather similar so we reuse the code

                if case3B or (not isinstance(aNodeParent, ast.If)):
                    """
                    Case 4: aNodeParent is not ast.If

                    Ex:
                      if matchingPredicate:
                          ...
                      el...:
                          ...

                    Therefore:
                      - we insert in aNodeParent.body, at the right place:
                          - if orelse is an ast.If:
                              we insert this particular ast.If
                          - if orelse is an else:
                              we insert a list of statements
                    """
                    print "Replacement case #4"

                    if (len(aNode.orelse) == 1) and \
                                        isinstance(aNode.orelse[0], ast.If):
                        """
                        Case 4a (with aNodeParent is not ast.If) or 3.b.a and:
                          if matchingPredicate:
                              ...
                          elif orelsePredicate:
                              ...
                        """

                        myIndex = -1
                        try:
                            myIndex = aNodeParent.body.index(aNode)
                        except:
                            print "Exception once: Am here!!!!"


                        if MY_DEBUG_STDOUT:
                            print "myIndex = %d" % myIndex

                        if myIndex >= 0: #This should always be true.
                            # !!!!This is just 1 statement - copy all.
                            aNodeParent.body[myIndex] = copy.deepcopy(aNode.orelse[0])

                        # else is a second (or later) elif

                        #aNodeParent.body = copy.deepcopy(orelse[1])
                    elif isinstance(aNode.orelse, list):
                        """
                        Case 4b (when aNodeParent is not ast.If) or 3.b.b and:
                          if matchingPredicate:
                              ...
                          else:
                              ...

                        This is the case with orelse a simple else branch:
                            if matching_predicate: ... else: ...

                        This is an else branch, without any test - we
                            do not modify it 
                            (it would have been actually difficult,
                                since orelse is an unmutable tuple).

                        !!!!If orelse is an else branch, and the aNode
                            is an if and not elif, then put it's
                            statement list without any if statement (aNode
                            becomes statement list without if
                            in aNodeParent), orelse is removed).
                        """

                        # We insert the statement list instead in the
                        #   aNodeParent.body of the aNode statement.
                        """
                        if MY_DEBUG_STDOUT:
                            print "aNodeParent.body = %s" \
                                    % str(aNodeParent.body)
                        """

                        if MY_DEBUG_STDOUT:
                            print "This is the case with orelse a simple " \
                                    "else branch"

                        """
                        We remove the if branch, then copy the content of the
                            orelse in the parent block (at the right place).
                        """
                        try:
                            myIndex = aNodeParent.body.index(aNode)
                        except:
                            if MY_DEBUG_STDERR:
                                traceback.print_exc()
                                sys.stderr.flush()
                            myIndex = -1

                        if MY_DEBUG_STDOUT:
                            print "aNodeParent.body=", aNodeParent.body
                            print "aNode =", aNode
                            print "myIndex = %d" % myIndex

                        if myIndex >= 0:
                            aNodeParent.body.remove(aNode)

                            for orelseIndex in range(len(aNode.orelse)):
                                #print "myIndex = %d, " \
                                #       "orelseIndex = %d" \
                                #       % (myIndex, orelseIndex)

                                aNodeParent.body.insert(myIndex + orelseIndex,
                                                     aNode.orelse[orelseIndex])
                                #myIndex += 1

                            CheckIfParentEmptyToAddPass(aNodeParent, aNode)
                        else:
                            #print "Exception here!!!!"

                            """
                            This happened most likely becase the
                                parent is an if statement (as well).
                            This could be the case with:
                                if (not matching_predicate):...
                                elif matching_predicate:...
                                else:...
                            """

                            #!!!!It doens't seem this is right:
                            #   aNodeParent.orelse[1][0] = copy.deepcopy(orelse[1][0])

                            #(test, body, parentOrelse) = \
                            #    GetElementsOfIf(aNodeParent,
                            #       howMuchIndent)
                            #aNodeParent.orelse[1] = copy.deepcopy(eliforelse[1])

                            if isinstance(aNodeParent, ast.If):
                                """
                                Note: aNodeParent.orelse is the list of
                                    statements, while orelse = ('orelse', list
                                    of statements)
                                """

                                #aNodeParent.orelse = orelse[1]
                                aNodeParent.orelse = aNode.orelse

            """
            In case due to the removal performed above
                (or even from the beginning) the parent is an
                ast.FunctionDef with empty body, we add a pass statement.
            """
            if isinstance(aNodeParent, ast.FunctionDef):
                if aNodeParent.body == []:
                    myStmt = ast.Pass()
                    aNodeParent.body = [ myStmt ]
                    
            # For some scripts (e.g., test10.py) it is better to give return.
            #if RETURN_AFTER_CHANGE_AST:
            #    return

        #print ast.iter_fields(aNode)[0]
        #print ast.iter_fields(aNode)[0]

        #    aNode._fields only conatains the field, without value, it seems
        #print "            " + str(aNode._fields)

    if MY_DEBUG_STDOUT:
        for myIndex in range(howMuchIndent):
            sys.stdout.softspace = 0
            print "    ",

        print "VisitAST(): at exit, aNode = %s, " \
                "ast.iter_child_nodes(aNode) = %s." % \
                    (str(aNode), str(ast.iter_child_nodes(aNode)))

    """
    From http://docs.python.org/library/ast.html:
        "Yield all direct child nodes of node, that is, all fields that are
        nodes and all items of fields that are lists of nodes."
    """
    # !!!!For if/elif/else MAYBE we should take care and not process (again)
    #    the test element, at least:
    for childNode in ast.iter_child_nodes(aNode):
        VisitAST(childNode, aNode, howMuchIndent + 1)



mainLevelFunctionsList = []
def VisitAST_ToGetMainLevelFunctions(aNode, aNodeParent):
    global mainLevelFunctionsList

    if isinstance(aNode, ast.FunctionDef) and \
            isinstance(aNodeParent, ast.Module):

        if False:
            if MY_DEBUG_STDOUT:
                #print "aNode =", aNode
                print "aNode.name =", aNode.name
                #print "aNode.args =", aNode.args
                #print "dir(aNode) =", dir(aNode)
                #print "aNode.body = %s" % aNode.body
                #print "aNodeParent =", aNodeParent

        mainLevelFunctionsList.append(aNode.name)

        """
        !!!!TODO: Note that we can run multiple -e commands:
            sed -e "s/a/A/g" -e "s/b/B/g": build list and then build command .
        """

        """
        We want to rename function names s.t. they have first character
            small letter:
        """
        print 'sed -e "s/%s/%s/g" $1 >$1.aux' % (aNode.name, \
                str.lower(aNode.name[0 : 1]) + aNode.name[1 : ])
        print 'rm -f $1'
        print 'mv $1.aux $1'


    for childNode in ast.iter_child_nodes(aNode):
        VisitAST_ToGetMainLevelFunctions(childNode, aNode)


def MainWithAST():
    global MY_DEBUG_STDOUT

    """
    From http://docs.python.org/library/ast.html:
        Parse the source into an AST node.
        Equivalent to compile(source, filename, mode, ast.PyCF_ONLY_AST).
    """
    #astRoot = ast.parse(source, filename = "<unknown>", mode = "exec")
    astRoot = ast.parse(source, filename = "not relevant", mode = "exec")

    #if True:
    if False:
        VisitAST_ToGetMainLevelFunctions(astRoot, None)
        resStr = "sed "
        for aName in mainLevelFunctionsList:
            resStr += '-e "s/%s/%s/g"' \
                        % (aName, str.lower(aName[0 : 1]) + \
                           aName[1 : ])

        resStr += " $1 >$1.aux'"
        return

    if False:
        if MY_DEBUG_STDOUT:
            print "astRoot =", astRoot

        """
        From http://docs.python.org/library/ast.html:
        "Recursively yield all descendant nodes in the tree starting at node
            (including node itself), in no specified order.
        This is useful if you only want to modify nodes in place and don't
            care about the context."
        """
        ast.walk(astRoot)

        if MY_DEBUG_STDOUT:
            #print "ast.dump(astRoot, ...) = ", \
            #    ast.dump(astRoot, annotate_fields = True, \
            #    include_attributes = False)
            print "ast.dump(astRoot, ...) = ", \
                    ast.dump(astRoot, annotate_fields=False,
                                    include_attributes=False)

    #if False:
    if True:
        """
        We iterate until we don't change the AST anymore - since in VisitAST we
            give return after a transformation, to avoid complications with
            parsing the children of the transformed node - !!!!TRY IT again.
        """
        global numPass
        numPass = 0

        while True:
            global passChangedAST
            passChangedAST = False

            #if MY_DEBUG_STDOUT:
            print "Calling VisitAST() - pass #%d." % numPass
            numPass += 1

            if numPass == NUM_MAX_PASSES:
                MY_DEBUG_STDOUT = True
            #MY_DEBUG_STDOUT = True

            VisitAST(astRoot, None, howMuchIndent = 0)

            if MY_DEBUG_STDOUT:
                print "%s" % codegen.to_source(astRoot)

            #break
            # We don't go beyond NUM_MAX_PASSES iterations, since it might be
            #    looping forever in some strange cases.
            if (passChangedAST == False) or (numPass >= NUM_MAX_PASSES):
                if numPass >= NUM_MAX_PASSES:
                    print "Stopping removal because numPass = %d." % numPass
                elif passChangedAST == False:
                    print "Stopping removal because passChangedAST == False " \
                            "at numPass = %d." % numPass
                break

        #print "The transformed script is:"
        #print "%s" % codegen.to_source(astRoot)

        fileName2 = fileName + ".ppp"
        print "The transformed script can be found in %s" % fileName2
        fOutput = open(fileName2, "wt")
        fOutput.write(codegen.to_source(astRoot))
        fOutput.close()


    if False:
    #if True:
        astRoot2 = copy.deepcopy(astRoot)

        print "astRoot2 =", astRoot2

        #print "%s" % codegen.to_source(astRoot)
        print "%s" % codegen.to_source(astRoot2)





#"""
#PREDICATE_TO_KEEP_BLOCK =
#    ["crararararararNOT_TO_BE_FOUND_FOR_SURE ;)"]

#PREDICATE_TO_KEEP_BLOCK = ["iOS_PYOBJC"]


"""
# Here we keep ANDROID_OS and remove all the other platforms
PREDICATE_TO_KEEP_BLOCK = ["ANDROID_OS"]

PREDICATE_TO_REMOVE_BLOCK = [
                               # I guess I should remove this block
                               "SYMBIAN_OS",
                               "SYMBIAN_S60_OS",
                               "SYMBIAN_S60_2ND_ED",
                               "SYMBIAN_S60_3RD_ED",
                               "SYMBIAN_1",
                               "SYMBIAN_3",
                               "SYMBIAN_UIQ_OS",
                               #"ANDROID_OS",
                               "iOS_PYOBJC",
                               "iOS",
                               "WINDOWS_CE_OS_PYTHONCE",
                               "WINDOWS_OS",
                               "UNIX_OS"
                        ]
"""

#"""
# Here we keep 2nd ed Symbian OS code (2nd ed is represented by the
#   3 predicates in PREDICATE_TO_KEEP_BLOCK and remove all other platforms).
PREDICATE_TO_KEEP_BLOCK = [
                               "SYMBIAN_OS",
                               "SYMBIAN_S60_OS",
                               "SYMBIAN_S60_2ND_ED"
                        ]

PREDICATE_TO_REMOVE_BLOCK = [
                               "ANDROID_OS",
                               "SYMBIAN_S60_3RD_ED",
                               "SYMBIAN_1",
                               "SYMBIAN_3",
                               "SYMBIAN_UIQ_OS",
                               #"ANDROID_OS",
                               "iOS_PYOBJC",
                               "iOS",
                               "WINDOWS_CE_OS_PYTHONCE",
                               "WINDOWS_OS",
                               "UNIX_OS"
                        ]
#"""


"""
# Here we keep WINDOWS_OS code.
PREDICATE_TO_KEEP_BLOCK = [
                                "WINDOWS_OS"
                        ]

PREDICATE_TO_REMOVE_BLOCK = [
                               "ANDROID_OS",
                               "SYMBIAN_OS",
                               "SYMBIAN_S60_OS",
                               "SYMBIAN_S60_2ND_ED"
                               "SYMBIAN_S60_3RD_ED",
                               "SYMBIAN_1",
                               "SYMBIAN_3",
                               "SYMBIAN_UIQ_OS",
                               #"ANDROID_OS",
                               "iOS_PYOBJC",
                               "iOS",
                               "WINDOWS_CE_OS_PYTHONCE",                               
                               "UNIX_OS"
                        ]
"""


"""
# This removes (I guess) SYMBIAN_UIQ_OS - keep in mind that in very few cases I
#    have SYMBIAN_OS preceeding the SYMBIAN_S60_OS, but not it seems the
#    SYMBIAN_UIQ_OS predicate.

PREDICATE_TO_KEEP_BLOCK = [    "SYMBIAN_OS",
                                "SYMBIAN_UIQ_OS"]

PREDICATE_TO_REMOVE_BLOCK = [
                                # I guess I should remove this block
                                #"SYMBIAN_OS",
                                "SYMBIAN_S60_OS",
                                "SYMBIAN_S60_2ND_ED",
                                "SYMBIAN_S60_3RD_ED",
                                "SYMBIAN_1",
                                "SYMBIAN_3",
                                #"SYMBIAN_UIQ_OS",
                                "ANDROID_OS",
                                "iOS_PYOBJC",
                                "iOS",
                                "WINDOWS_CE_OS_PYTHONCE",
                                "WINDOWS_OS",
                                "UNIX_OS"
                                        ]
"""



#"""

"""
#LIST_PREDICATES_TO_BE_IN_EXPRESSION_AT_LEAST_ONE_TO_KEEP_BLOCK = []
PREDICATE_TO_KEEP_BLOCK = []

#LIST_PREDICATES_TO_BE_IN_EXPRESSION_AT_LEAST_ONE_TO_REMOVE_BLOCK = []
PREDICATE_TO_REMOVE_BLOCK = []
"""


"""
PREDICATE_TO_KEEP_BLOCK = [sys.argv[2]]
if len(sys.argv) >= 4:
    PREDICATE_TO_KEEP_BLOCK = sys.argv[3]
else:
    PREDICATE_TO_KEEP_BLOCK = None
"""

fileName = sys.argv[1]
source = open(fileName, "rU").read()
source_no_encoding = source


MainWithAST()
